<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------

namespace app\services\crud;

use app\dao\crud\QuestionDao;
use app\dao\crud\QuestionOptionDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\exceptions\ApiException;
use think\facade\Db;

/**
 * 题目业务逻辑层
 * Class QuestionServices
 * @package app\services\crud
 */
class QuestionServices extends BaseServices
{
    /**
     * QuestionServices constructor.
     * @param QuestionDao $dao
     */
    public function __construct(QuestionDao $dao)
    {
        $this->dao = $dao;
    }

    /**
     * 获取题目列表
     * @param array $where
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getList(array $where)
    {
        [$page, $limit] = $this->getPageValue();
        
        $where['is_del'] = 0;
        
        $list = $this->dao->getList($where, $page, $limit);
        $count = $this->dao->count($where);

        return compact('list', 'count');
    }

    /**
     * 获取题目详情
     * @param int $id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getInfo(int $id)
    {
        $info = $this->dao->getQuestionWithOptions($id);
        
        if (empty($info)) {
            throw new AdminException('题目不存在');
        }

        return $info;
    }

    /**
     * 创建题目
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    public function create(array $data)
    {
        // 检查题目编号是否存在
        if ($this->dao->isQuestionNoExists($data['question_no'])) {
            throw new AdminException('题目编号已存在');
        }

        // 验证选项数据
        if (empty($data['options']) || !is_array($data['options'])) {
            throw new AdminException('请添加题目选项');
        }

        // 开启事务
        Db::startTrans();
        try {
            // 创建题目
            $questionData = [
                'question_no' => $data['question_no'],
                'title' => $data['title'],
                'type' => $data['type'],
                'category_id' => $data['category_id'] ?? 0,
                'status' => $data['status'] ?? 1,
                'is_required' => $data['is_required'] ?? 1,
                'sort' => $data['sort'] ?? 0,
                'remark' => $data['remark'] ?? '',
            ];

            $question = $this->dao->save($questionData);
            if (!$question) {
                throw new AdminException('创建题目失败');
            }

            // 保存选项
            /** @var QuestionOptionDao $optionDao */
            $optionDao = app()->make(QuestionOptionDao::class);
            $result = $optionDao->batchSaveOptions($question->id, $data['options']);

            if (!$result) {
                throw new AdminException('保存选项失败');
            }

            Db::commit();
            return $question->id;
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 更新题目
     * @param int $id
     * @param array $data
     * @return bool
     * @throws \Exception
     */
    public function update(int $id, array $data)
    {
        $question = $this->dao->get($id);
        if (!$question) {
            throw new AdminException('题目不存在');
        }

        // 检查题目编号是否存在
        if (isset($data['question_no']) && $this->dao->isQuestionNoExists($data['question_no'], $id)) {
            throw new AdminException('题目编号已存在');
        }

        // 开启事务
        Db::startTrans();
        try {
            // 更新题目
            $questionData = [];
            $allowFields = ['question_no', 'title', 'type', 'category_id', 'status', 'is_required', 'sort', 'remark'];
            foreach ($allowFields as $field) {
                if (isset($data[$field])) {
                    $questionData[$field] = $data[$field];
                }
            }

            if (!empty($questionData)) {
                $this->dao->update($id, $questionData);
            }

            // 如果有选项数据，更新选项
            if (isset($data['options']) && is_array($data['options'])) {
                /** @var QuestionOptionDao $optionDao */
                $optionDao = app()->make(QuestionOptionDao::class);
                $result = $optionDao->batchSaveOptions($id, $data['options']);

                if (!$result) {
                    throw new AdminException('更新选项失败');
                }
            }

            Db::commit();
            return true;
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 删除题目
     * @param int $id
     * @return bool
     */
    public function delete(int $id)
    {
        $question = $this->dao->get($id);
        if (!$question) {
            throw new AdminException('题目不存在');
        }

        // 软删除
        Db::startTrans();
        try {
            $this->dao->update($id, ['is_del' => 1]);

            // 删除选项
            /** @var QuestionOptionDao $optionDao */
            $optionDao = app()->make(QuestionOptionDao::class);
            $optionDao->deleteByQuestionId($id);

            Db::commit();
            return true;
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 修改题目状态
     * @param int $id
     * @param int $status
     * @return bool
     */
    public function setStatus(int $id, int $status)
    {
        $question = $this->dao->get($id);
        if (!$question) {
            throw new AdminException('题目不存在');
        }

        return $this->dao->update($id, ['status' => $status]) ? true : false;
    }

    /**
     * 获取指定类型的题目列表（前台使用）
     * @param int $type
     * @param int $categoryId
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getQuestionsByType(int $type, int $categoryId = 0)
    {
        $where = [];
        if ($categoryId > 0) {
            $where['category_id'] = $categoryId;
        }

        $list = $this->dao->getListByType($type, $where);

        // 根据题目类型处理数据
        foreach ($list as &$item) {
            if (isset($item['options']) && is_array($item['options'])) {
                foreach ($item['options'] as &$option) {
                    // 解析题只返回选项内容，隐藏解析（答题后才显示）
                    if ($type == 1) {
                        $option['analysis_hidden'] = true;
                    }
                    // 分值题隐藏分值（后台计算用）
                    if ($type == 2) {
                        $option['score_hidden'] = true;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * 获取题目的选项解析（用户答题后调用）
     * @param int $questionId
     * @param int $optionId
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getOptionAnalysis(int $questionId, int $optionId)
    {
        $question = $this->dao->getQuestionWithOptions($questionId);
        
        if (empty($question)) {
            throw new ApiException('题目不存在');
        }

        // 检查是否是解析题
        if ($question['type'] != 1) {
            throw new ApiException('该题目不支持解析');
        }

        // 查找对应选项
        $selectedOption = null;
        foreach ($question['options'] as $option) {
            if ($option['id'] == $optionId) {
                $selectedOption = $option;
                break;
            }
        }

        if (!$selectedOption) {
            throw new ApiException('选项不存在');
        }

        return [
            'question_id' => $questionId,
            'option_id' => $optionId,
            'option_key' => $selectedOption['option_key'],
            'option_content' => $selectedOption['option_content'],
            'analysis' => $selectedOption['analysis'] ?? '',
        ];
    }

    /**
     * 计算答题得分（用于分值题）
     * @param array $answers 答案数组 [['question_id' => 1, 'option_id' => 1], ...]
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function calculateScore(array $answers)
    {
        $totalScore = 0;
        $details = [];

        /** @var QuestionOptionDao $optionDao */
        $optionDao = app()->make(QuestionOptionDao::class);

        foreach ($answers as $answer) {
            $questionId = $answer['question_id'] ?? 0;
            $optionId = $answer['option_id'] ?? 0;

            if ($questionId <= 0 || $optionId <= 0) {
                continue;
            }

            // 获取题目信息
            $question = $this->dao->get($questionId);
            if (!$question || $question['type'] != 2) {
                continue;
            }

            // 获取选项信息
            $option = $optionDao->get($optionId);
            if (!$option || $option['question_id'] != $questionId) {
                continue;
            }

            $score = floatval($option['score']);
            $totalScore += $score;

            $details[] = [
                'question_id' => $questionId,
                'question_no' => $question['question_no'],
                'option_id' => $optionId,
                'option_key' => $option['option_key'],
                'score' => $score,
            ];
        }

        return [
            'total_score' => $totalScore,
            'total_questions' => count($details),
            'details' => $details,
        ];
    }
}

